/**
 * 
 */
package com.sensor;

import com.sensor.RSFFrame.OpType;
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InterruptedIOException;
import java.math.BigInteger;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.InetSocketAddress;
import java.net.ServerSocket;
import java.net.Socket;
import java.net.InetAddress;
import java.net.SocketAddress;
import java.net.UnknownHostException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;


/**
 * @author Mac
 *
 */
public class Sensor {
	public enum NetType{
		UNKNOWN(0),
		UDP(1),
		TCP(2),
		SNMP(3);
		
		private int value = 0;
	    private NetType(int value) {    //    ������private�ģ�����������
	        this.value = value;
	    }
	    public static NetType valueOf(int value) {    //    ��д�Ĵ�int��enum��ת������
	        switch (value) {
	        case 0:
	        	return UNKNOWN;
	        case 1:
	            return UDP;
	        case 2:
	            return TCP;
	        case 3:
	        	return SNMP;
	        default:
	            return UNKNOWN;
	        }
	    }

	    public int value() {
	        return this.value;
	    }
	}
	
	//class 
	private final int mFlag = 0x20202958;
	private InetAddress 	mIpAddr = null;
	private int				mPort = 10030;
	private byte[] 			mHwMac = new byte[6];
	private RSFFrame 		mInitFrame = new RSFFrame();
	private Object 			mSock = null;
	private NetType 		mType = NetType.UNKNOWN;
	private ArrayList<Sensor.NetType> 	mSupportProtocolType = new ArrayList<Sensor.NetType>();
	
	public Sensor(InetAddress IpAddr, int port) {
		super();
		this.mIpAddr = IpAddr;
		this.mPort = port;
	}
	
	public Sensor(InetAddress IpAddr, int port, byte[] HwMac) {
		super();
		this.mIpAddr = IpAddr;
		this.mHwMac = HwMac;
		this.mPort = port;
	}
	
	
	
	public Sensor(RSFFrame rsf, int port) throws UnknownHostException{
		super();
		this.mIpAddr = InetAddress.getByAddress(BigInteger.valueOf(rsf.src_ip_addr).toByteArray());
		this.mHwMac = rsf.src_hw_addr;
		this.mPort = port;
		mInitFrame = rsf;
	}
	
	public String GetVersion(){
		String retval = new String("");
		retval = "(Mode��TH58x9)";
		return retval;
	}
	
	final public int GetHardwareFlag(){
		return mFlag;
	}
	/**
	 * 
	 * @return
	 */
	public byte[] GetHardwareMac(){
		return mHwMac;
	}
	/**
	 * 
	 * @return
	 * @throws IOException
	 */
	public UDPPacket GetCurrentStatus() throws IOException{
		byte[] msg = new byte[53];
		msg[0] = (byte)0xaa;
		msg[1] = (byte)0xaa;
		
		send(msg, mPort);
		byte[] buffer = new byte[100];
		DatagramPacket packet = new DatagramPacket(buffer, buffer.length);
		DatagramSocket sock = (DatagramSocket)mSock;
		sock.setSoTimeout(2000);
		UDPPacket pc = null;
		try {
			sock.receive(packet);
			pc = new UDPPacket();
			pc.FromByteStream(packet.getData());
		} catch (InterruptedIOException e){
			
		}		
		return pc;
	}
	
	/**
	 *  ��IPһ����˵ �� GetIpAddress��һ�µģ���Ҳ�����, orignal ip�����õ�ͨ��ip, ��ipaddress����ÿ���豸
	 *  ͨ�Ŵ�ص�ַ
	 * @return
	 * @throws IOException
	 */
	public String GetIpAddress() throws IOException{
		return mIpAddr.toString();
	}	

	/**
	 * 
	 * @return
	 */
	public String GetHardwareAddress(){
		return UDPPacket.bytesToHexString(mHwMac);
	}
	/**
	 * 
	 * @param IpAddr
	 * @return
	 * @throws UnknownHostException
	 */
	public RSFFrame SetIpAddress(InetAddress IpAddr) throws UnknownHostException {
		RSFFrame f = new RSFFrame();
		f = mInitFrame;
		f.SetOpCode(OpType.SETIP);

		int val = 0;
		for (int i = 0; i < IpAddr.getAddress().length; i++) {
			val <<= 8;
		    val |= IpAddr.getAddress()[i] & 0xff;
		}
		f.src_ip_addr = val;
		f.dst_ip_addr = val;
		return f;
	}
	/**
	 * 
	 * @param type
	 * @param ip_addr
	 * @param port
	 * @throws IOException
	 */
	public void SetNetwork(NetType type, InetAddress ip_addr, int port) throws IOException{
		mIpAddr = ip_addr;
		mPort = port;
		if (mSock != null){
			System.out.print(mSock.getClass().getName());
			if (mSock.getClass().getName() == "Socket"){
				Socket sock = (Socket)mSock;
				if (sock.isClosed()){
					sock.close();
				}
			} else if (mSock.getClass().getName() == "DatagramSocket"){
				DatagramSocket sock = (DatagramSocket)mSock;
				
				if (!sock.isClosed()){
					sock.close();
				}
			}
			mType = NetType.UNKNOWN;
		}
		if (type == NetType.UDP){		// udp
			mSock = new DatagramSocket(port);
			mType = NetType.UDP;
			
		} else if (type == NetType.TCP){ //tcp
			mSock = new Socket(null);
			SocketAddress sock_addr = new InetSocketAddress(ip_addr.toString(), port);
			Socket tcp_sock = (Socket)mSock;
			tcp_sock.connect(sock_addr);
			
			mType = NetType.TCP;
		} else {
			mType = NetType.UNKNOWN;
		}
		
	}
	/**
	 * 
	 * @param type
	 * @throws IOException
	 */
	public void SetNetworkType(NetType type) throws IOException{
		SetNetwork(type, mIpAddr, mPort);
	}

	/**
	 * 
	 * @param ip
	 * @param port
	 * @throws IOException
	 */
	public void SetIp(InetAddress ip, int port) throws IOException{
		RSFFrame f = SetIpAddress(ip);
		send(f.GetByteStream(), port);
	}
	public void Ioctl(RSFFrame frame){
		
	}
	/**
	 * this will clear protocol first, then add new list
	 * @param protocol_list
	 */
	public void SetSupportProtocol(ArrayList<Sensor.NetType> protocol_list){
		mSupportProtocolType.clear();
		for (Sensor.NetType prot : protocol_list){
			mSupportProtocolType.add(prot);
		}
	}
	
	public Map getDevInfo() throws IOException {
        Map<String, Object> map = null;
        UDPPacket pack = GetCurrentStatus();
        if (pack != null){
        	map = new HashMap<String, Object>();
            map.put("mac", pack.GetMac());
            map.put("ip", pack.GetIP());
            map.put("temperature", pack.GetTemperature());
            map.put("humiduty", pack.GetHumiduty());
        }
        return map;
	}
	@Override
	public String toString(){
		String retval = new String();
		
		retval = "(device_mode: " + GetVersion()
				+ ", mac: 0x" + UDPPacket.bytesToHexString(mHwMac)
				+ ", ip: " + mIpAddr.toString()
				+ ", port: " + mPort
				+ ")";
		
		return retval;
	}
	/**
	 * 
	 * @param msg
	 * @param port
	 * @throws IOException
	 */
	private void send(byte[] msg, int port) throws IOException {
		
		if (mType == NetType.UNKNOWN){
		} else if (mType == NetType.UDP){
			DatagramSocket udp_sock = (DatagramSocket)mSock;
			DatagramPacket dp = new DatagramPacket(msg, msg.length,
					this.mIpAddr, port);
			udp_sock.send(dp);
		} else {
		}
	}
	
}

